/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;
import javax.swing.text.JTextComponent;

import org.apache.log4j.Logger;

import who.tmap.bo.ThematicMap;
import who.tmap.ui.util.DataTableDataModel;
import who.tmap.ui.util.jsorttable.CellRenderer;
import who.tmap.ui.util.jsorttable.JSortTable;
import who.tmap.ui.util.jsorttable.SortTableModel;
import who.tmap.util.MiscHelper;

/**
 * A dialog dedicated to thematic map data edition
 * @author stl
 *
 */
public class JFrameData extends JInternalFrame 
					 	implements Printable {//, SimpleObserved {

    private static Logger logger = Logger.getLogger(JFrameData.class);  
    
    //private SimpleObservedImpl observedImpl =new SimpleObservedImpl();
    
	private JTable jTable = null;
	private boolean wizardMode =false;
	private TableModel dataModel =null;  
	private ThematicMap thematicMap =null;
	
	private JFrameData thisFrame =this;
	
	private JPanel jPanelButton = null;
	private JButton jButtonAddRow = null;
	private JButton jButtonDelRow = null;

	private JScrollPane jScrollPane = null;

	private JPanel jPanelButtons = null;
	
	public JFrameData() {
		super("Data");

		initialize();
	}

	private void initialize() {
		this.setSize(300, 200);
				
		getContentPane().setLayout(new BorderLayout());

		getContentPane().add( getJScrollPane(), BorderLayout.CENTER);		
		getContentPane().add( getJPanelButtons(), BorderLayout.SOUTH );
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setClosable( false );
		
		setFrameIcon( new MiscHelper().getImageIcon("ic_attrib2.gif") );
	}
	
	public JTable getJTable() {
		if ( jTable == null ) {
		    dataModel = new DataTableDataModel();
			
			jTable = new JSortTable( (SortTableModel)dataModel ) {
				 
				//  Select the text when the cell starts editing
				//  a) text will be replaced when you start typing in a cell
				//  b) text will be selected when you use F2 to start editing
				//  c) caret is placed at end of text when double clicking to start editing
				public Component prepareEditor(
					TableCellEditor editor, int row, int column)
				{
					Component c = super.prepareEditor(editor, row, column);
	 
					if (c instanceof JTextComponent)
					{
						((JTextField)c).selectAll();
					}
	 
					return c;
				}
			};
			
			CellRenderer cr =new CellRenderer();
			jTable.setDefaultRenderer(Object.class, cr);
			
			InputMap im = jTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
			 
			KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
	 
			final Action oldTabAction = (Action) jTable.getActionMap().get(im.get(tab));
			Action tabAction = new AbstractAction()
			{
				public void actionPerformed(ActionEvent e)
				{
					JTable table = (JTable)e.getSource();
					int originalRow = table.getSelectedRow();
					int originalColumn = table.getSelectedColumn();
					
					oldTabAction.actionPerformed( e );
					
					int row = table.getSelectedRow();
					int column = table.getSelectedColumn();					
	 
					if ( row == 0 && column == 0 ) {
						((DataTableDataModel)dataModel).insertRowAt( -1 );
						table.changeSelection(table.getRowCount() -1, 0, false, false);
					} 
				}
			};
			jTable.getActionMap().put(im.get(tab), (Action) tabAction);
			
			KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
			final Action oldEnterAction = (Action) jTable.getActionMap().get(im.get(enter));
			Action enterAction = new AbstractAction()
			{
				public void actionPerformed(ActionEvent e)
				{
					JTable table = (JTable)e.getSource();
					int originalRow = table.getSelectedRow();
					int originalColumn = table.getSelectedColumn();
					
					oldEnterAction.actionPerformed( e );
					
					int row = table.getSelectedRow();
					int column = table.getSelectedColumn();					
	 
					if ( row == 0 && (column == originalColumn+1 || ( column == 0 && originalRow == table.getRowCount()-1 ) ) ) {
						((DataTableDataModel)dataModel).insertRowAt( -1 );
						table.changeSelection(table.getRowCount() -1, originalColumn, false, false);
					} 
				}
			};
			jTable.getActionMap().put(im.get(enter), (Action) enterAction);
			
			jTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

		}
		return jTable;
	}
	
	public void setWizardMode(boolean b) {
		wizardMode = b;
		
		setResizable( !wizardMode );
		setMaximizable( !wizardMode );
		setIconifiable( !wizardMode );
		
		if ( wizardMode ) {	
			setTitle( "Thematic Map Wizard - " + getTitle() );
		}
	}
	
	private JPanel getJPanelButton() {
		if(jPanelButton == null) {
			jPanelButton = new JPanel();

			GridLayout gridLayout1 = new GridLayout(1,2,5,5);
			jPanelButton.setLayout(gridLayout1);
			jPanelButton.add(getJButtonAddRow(), null);
			jPanelButton.add(getJButtonDelRow(), null);
		}
		return jPanelButton;
	}
	
	private JButton getJButtonAddRow() {
		if(jButtonAddRow == null) {
			jButtonAddRow = new JButton();
			jButtonAddRow.setText("Insert row");
			jButtonAddRow.addActionListener(new ActionListener() {   
				public void actionPerformed(ActionEvent e) {    
					int row =getJTable().getSelectedRow();
					int col =getJTable().getSelectedColumn();
					int newRow =((DataTableDataModel)dataModel).insertRowAt( row );
					
					// Make the new row visible (scroll if necessary)
			        JViewport viewport = (JViewport)getJTable().getParent();
			        
			        // This rectangle is relative to the table where the
			        // northwest corner of cell (0,0) is always (0,0).
			        Rectangle rect = getJTable().getCellRect(newRow, col, true);
			    
			        // The location of the viewport relative to the table
			        Point pt = viewport.getViewPosition();
			    
			        // Translate the cell location so that it is relative
			        // to the view, assuming the northwest corner of the
			        // view is (0,0)
			        rect.setLocation( rect.x - pt.x, rect.y - pt.y );
			    
			        // Scroll the area into view
			        viewport.scrollRectToVisible(rect);
			        
			        getJTable().requestFocusInWindow();
					getJTable().changeSelection( newRow, col != -1 ? col : 0, false, false );
						
					Component comp = getJTable().getEditorComponent();
					if ( comp != null ){
						comp.requestFocus();
					}

				} 
			
			});
		}
		return jButtonAddRow;
	}
	
	private JButton getJButtonDelRow() {
		if(jButtonDelRow == null) {
			jButtonDelRow = new JButton();
			jButtonDelRow.setText("Delete row");
			jButtonDelRow.addActionListener(new ActionListener() { 
				public void actionPerformed(ActionEvent e) {

					if ( getJTable().getSelectedRowCount() != 0 ) {
						int row =getJTable().getSelectedRow();
						int col =getJTable().getSelectedColumn();
						
						int[] selectedRows =getJTable().getSelectedRows();
						
						if ( getJTable().isEditing() ) {
							getJTable().getCellEditor().cancelCellEditing();
						}
						
					    ((DataTableDataModel)dataModel).removeRows( selectedRows );
					    
					    if ( getJTable().getRowCount() > 0 ) {
					    	if ( row > getJTable().getRowCount()-1 ) {
					    		row--;
					    	}
					    	
					        getJTable().requestFocusInWindow();
							getJTable().changeSelection( row != -1 ? row : 0, col != -1 ? col : 0, false, false );
								
							Component comp = getJTable().getEditorComponent();
							if ( comp != null ){
								comp.requestFocus();
							}
					    }
					}
				}
			});
		}
		return jButtonDelRow;
	}
	
	private JScrollPane getJScrollPane() {
		if (jScrollPane == null) {
			jScrollPane = new JScrollPane();
			jScrollPane.setViewportView(getJTable());
			jScrollPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
				
		}
		return jScrollPane;
	}
	
	/**
	 * Print a JTable
	 * Code found at
	 * http://java.sun.com/developer/qow/archive/30/index.html
	 */
    public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
        Graphics2D  g2 = (Graphics2D) g;
    	g2.setColor(Color.black);
    	
    	int fontHeight=g2.getFontMetrics().getHeight();
    	int fontDesent=g2.getFontMetrics().getDescent();

    	//leave room for page number
    	double pageHeight = 
    	    pageFormat.getImageableHeight()-fontHeight;

    	double pageWidth = pageFormat.getImageableWidth();
    	double tableWidth =(double) getJTable().getColumnModel().getTotalColumnWidth();
    	double scale = 1; 
    	if (tableWidth >= pageWidth) {
    		scale =  pageWidth / tableWidth;
    	}

    	double headerHeightOnPage=getJTable().getTableHeader().getHeight()*scale;
    	double tableWidthOnPage=tableWidth*scale;

    	double oneRowHeight=(getJTable().getRowHeight() + getJTable().getRowMargin())*scale;
    	int numRowsOnAPage=
    	    (int)((pageHeight-headerHeightOnPage)/oneRowHeight);
    	double pageHeightForTable=oneRowHeight*numRowsOnAPage;
    	int totalNumPages= (int)Math.ceil(((double)getJTable().getRowCount())/numRowsOnAPage);
    	if ( pageIndex >= totalNumPages ) {
    		return Printable.NO_SUCH_PAGE;
    	}

    	g2.translate(pageFormat.getImageableX(), 
    	    pageFormat.getImageableY());
    	g2.drawString("Page: "+(pageIndex+1),(int)pageWidth/2-35, (int)(pageHeight+fontHeight-fontDesent));//bottom center

    	g2.translate(0f,headerHeightOnPage);
    	g2.translate(0f,-pageIndex*pageHeightForTable);
    	//TODO this next line treats the last page as a full page
    	g2.setClip(0, (int)(pageHeightForTable*pageIndex),(int) 
    	    Math.ceil(tableWidthOnPage), (int) Math.ceil(pageHeightForTable));

    	g2.scale(scale,scale);
    	getJTable().paint(g2);
    	g2.scale(1/scale,1/scale);
    	g2.translate(0f,pageIndex*pageHeightForTable);
    	g2.translate(0f, -headerHeightOnPage);
    	g2.setClip(0, 0,(int) Math.ceil(tableWidthOnPage), 
    		(int)Math.ceil(headerHeightOnPage));
    	g2.scale(scale,scale);
    	getJTable().getTableHeader().paint(g2);//paint header at top

    	return Printable.PAGE_EXISTS;
    }
    
	public ThematicMap getThematicMap() {
		return thematicMap;
	}
	
	public void setThematicMap(ThematicMap thematicMap) {
		this.thematicMap = thematicMap;
		
		if ( thematicMap != null ) {
			thematicMap.setData( ((DataTableDataModel)dataModel).getAttrs() );
		}		
	}
	
	public void enableWizardMode( boolean enabled ) {
		setIconifiable( !enabled );
		setMaximizable( !enabled );		
	}
	
	/*public void registerObserver(SimpleObserver observer) {
		observedImpl.registerObserver( observer );
	}
	
	public void unregisterObserver(SimpleObserver observer) {
		observedImpl.unregisterObserver( observer );
	}	
	
	public void notifyObservers( Object msg ) {
		observedImpl.notifyObservers( msg );
	}
    
    public void notifyObservers() {
    	//observedImpl.notifyObservers( ((DataTableDataModel)dataModel).getAttrs().getHeader() );
    }*/
    
    public void setDirty( boolean dirty ) {
    	((DataTableDataModel)dataModel).setDirty( dirty );
    }
    
    public boolean isDirty() {
    	return ((DataTableDataModel)dataModel).isDirty();
    }

	private JPanel getJPanelButtons() {
		if (jPanelButtons == null) {
			jPanelButtons = new JPanel();
			jPanelButtons.add(getJPanelButton(), null);
		}
		return jPanelButtons;
	}
}
